package com.newegg.datafeed.adserving.adwords;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;

import com.google.api.ads.adwords.axis.utils.v201809.SelectorBuilder;
import com.google.api.ads.adwords.axis.v201809.cm.ApiException;
import com.google.api.ads.adwords.axis.v201809.cm.Budget;
import com.google.api.ads.adwords.axis.v201809.cm.BudgetBudgetDeliveryMethod;
import com.google.api.ads.adwords.axis.v201809.cm.BudgetOperation;
import com.google.api.ads.adwords.axis.v201809.cm.BudgetServiceInterface;
import com.google.api.ads.adwords.axis.v201809.cm.Campaign;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroup;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroupOperation;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroupPage;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroupPerformanceTarget;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroupPerformanceTargetOperation;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroupPerformanceTargetPage;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroupPerformanceTargetServiceInterface;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignGroupServiceInterface;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignOperation;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignPage;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignReturnValue;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignServiceInterface;
import com.google.api.ads.adwords.axis.v201809.cm.EfficiencyTargetType;
import com.google.api.ads.adwords.axis.v201809.cm.Money;
import com.google.api.ads.adwords.axis.v201809.cm.Operator;
import com.google.api.ads.adwords.axis.v201809.cm.PerformanceTarget;
import com.google.api.ads.adwords.axis.v201809.cm.Selector;
import com.google.api.ads.adwords.axis.v201809.cm.SpendTargetType;
import com.google.api.ads.adwords.axis.v201809.cm.VolumeGoalType;
import com.google.api.ads.adwords.lib.selectorfields.v201809.cm.CampaignField;
import com.google.api.ads.adwords.lib.selectorfields.v201809.cm.CampaignGroupField;
import com.google.api.ads.adwords.lib.selectorfields.v201809.cm.CampaignGroupPerformanceTargetField;
import com.google.api.ads.common.lib.exception.ValidationException;

public class CampaignGroupService extends ADWordsParent {
	
	private static final int PAGE_SIZE = 100;

	CampaignGroupServiceInterface campaignGroupService;
	CampaignServiceInterface campaignService;
	CampaignGroupPerformanceTargetServiceInterface campaignGroupPerformanceTargetService;
	BudgetServiceInterface budgetService;

	public CampaignGroupService(String customerId) throws ValidationException {
		campaignGroupService = get(customerId, CampaignGroupServiceInterface.class);
		campaignService = get(customerId, CampaignServiceInterface.class);
		campaignGroupPerformanceTargetService = get(customerId, CampaignGroupPerformanceTargetServiceInterface.class);
		budgetService = get(customerId, BudgetServiceInterface.class);
	}

	/**
	 * 创建广告系列组
	 * @param name 广告系列组名称
	 * @return 广告系列组
	 */
	public CampaignGroup createCampaignGroup(String name) throws ApiException, RemoteException {
	  // 创建广告系列组
	  CampaignGroup campaignGroup = new CampaignGroup();
	  campaignGroup.setName(name);
	  // 指定操作类型
	  CampaignGroupOperation operation = new CampaignGroupOperation();
	  operation.setOperand(campaignGroup);
	  operation.setOperator(Operator.ADD); // 新建

	  CampaignGroup newCampaignGroup =
	      campaignGroupService.mutate(new CampaignGroupOperation[] {operation}).getValue(0);

	  return newCampaignGroup;
	}

	/**
	 * 添加广告系列到广告系列组
	 * <ul>
	 * <li>每个广告系列一次只能加入一个广告系列组</li>
	 * <li>您无法将使用共享预算的广告系列添加到广告系列组</li>
	 * </ul>
	 * @param campaignGroup 广告系列组
	 * @param campaignIds 要添加的广告系列
	 */
	public CampaignReturnValue addCampaignsToGroup(CampaignGroup campaignGroup, List<Long> campaignIds) throws ApiException, RemoteException {
		List<CampaignOperation> operations = new ArrayList<>();
		for (Long campaignId : campaignIds) {
			Campaign campaign = new Campaign();
			campaign.setId(campaignId);
			campaign.setCampaignGroupId(campaignGroup.getId());

			CampaignOperation operation = new CampaignOperation();
			operation.setOperand(campaign);
			operation.setOperator(Operator.SET);
			operations.add(operation);
		}

		CampaignReturnValue returnValue = campaignService
				.mutate(operations.toArray(new CampaignOperation[operations.size()]));
		
		return returnValue;
	}

	/**
	 * 创建效果目标
	 * <p>创建广告系列组后，您可以向其添加一个或多个效果目标。在您设置效果目标后，AdWords 可以预估您能否顺利实现目标。如果您很可能达不到所设置的目标，则可以考虑调整广告系列设置以改善效果。</p>
	 * 注意：效果目标仅跟踪广告系列的效果，不会影响投放。
	 * @param campaignGroup 广告系列组
	 * @param maxClicks 至少要达到的点击次数
	 * @param maxCpc 最大如CPC，每次点击成本
	 * @param maxSpendAmount 最大支出
	 * @param startDate 开始时间，格式：yyyyMMdd
	 * @param endDate 结束时间，格式：yyyyMMdd
	 * @return 效果目标
	 */
	public CampaignGroupPerformanceTarget createPerformanceTarget(CampaignGroup campaignGroup,
			Long maxClicks, Double maxCpc, Long maxSpendAmount, String startDate, String endDate) throws ApiException, RemoteException {

		// 创建效果目标
		CampaignGroupPerformanceTarget campaignGroupPerformanceTarget = new CampaignGroupPerformanceTarget();
		campaignGroupPerformanceTarget.setCampaignGroupId(campaignGroup.getId());

		PerformanceTarget performanceTarget = new PerformanceTarget();

		// 量：用于指定如何报告目标的实现进度，如，至少要达到的点击次数或转化次数
		performanceTarget.setVolumeTargetValue(maxClicks); // 预期值
		performanceTarget.setVolumeGoalType(VolumeGoalType.MAXIMIZE_CLICKS); // 使用点击次数衡量

		// 效率目标：对效果目标的“量”目标施加一种可选限制，通常是在支出方面，如，限制CPC(Cost Per Click)小于等于$3
		performanceTarget.setEfficiencyTargetType(EfficiencyTargetType.CPC_LESS_THAN_OR_EQUAL_TO); 
		performanceTarget.setEfficiencyTargetValue(maxCpc * 1_000_000);

		// 支出目标：对支出的金额施加一种可选限制，如，最多支出$50
		performanceTarget.setSpendTargetType(SpendTargetType.MAXIMUM);
		Money maxSpend = new Money();
		maxSpend.setMicroAmount(maxSpendAmount * 1_000_000); // 以客户所用币种的百万分之一为单位表示的金额
		performanceTarget.setSpendTarget(maxSpend);

		// 时间段：如果指定【效率目标】或【支出目标】，则还需要指定跟踪这些目标的时间范围，如，未来的90天内
		performanceTarget.setStartDate(startDate);
		performanceTarget.setEndDate(endDate);

		campaignGroupPerformanceTarget.setPerformanceTarget(performanceTarget);

		// 指定操作类型
		CampaignGroupPerformanceTargetOperation operation = new CampaignGroupPerformanceTargetOperation();
		operation.setOperand(campaignGroupPerformanceTarget);
		operation.setOperator(Operator.ADD);

		CampaignGroupPerformanceTarget newCampaignGroupPerformanceTarget = campaignGroupPerformanceTargetService
				.mutate(new CampaignGroupPerformanceTargetOperation[] { operation }).getValue(0);

		return newCampaignGroupPerformanceTarget;
	}

	/**
	 * 创建广告系列组设置效果目标
	 * @param campaignIds Campaign ID列表
	 * @param maxClicks 至少要达到的点击次数
	 * @param maxCpc 最大如CPC，每次点击成本
	 * @param maxSpendAmount 最大支出
	 * @param startDate 开始时间，格式：yyyyMMdd
	 * @param endDate 结束时间，格式：yyyyMMdd
	 * @return 效果目标
	 */
	public CampaignGroupPerformanceTarget createPerformanceTargetForCampaigns(List<Long> campaignIds,
			Long maxClicks, Double maxCpc, Long maxSpendAmount, String startDate, String endDate) throws ApiException, RemoteException {
		// 创建广告系列组
		CampaignGroup campaignGroup = createCampaignGroup("Newegg campaign group #" + System.currentTimeMillis());
		// 添加广告系列到广告系列组
		addCampaignsToGroup(campaignGroup, campaignIds);
		// 创建效果目标
		CampaignGroupPerformanceTarget newCampaignGroupPerformanceTarget = createPerformanceTarget(
				campaignGroup, maxClicks, maxCpc, maxSpendAmount, startDate, endDate);
		return newCampaignGroupPerformanceTarget;
	}

	/**
	 * 查询所有广告系列
	 */
	public List<Campaign> getAllCampaign() throws ApiException, RemoteException {

		List<Campaign> list = new ArrayList<Campaign>();

		int offset = 0;
		SelectorBuilder builder = new SelectorBuilder();
		Selector selector = builder.fields(CampaignField.values())
				.orderAscBy(CampaignField.Name)
				.offset(offset).limit(PAGE_SIZE).build();

		CampaignPage page;
		do {
			page = campaignService.get(selector);
			if (page.getEntries() != null) {
				for (Campaign campaign : page.getEntries()) {
					list.add(campaign);
				}
			}
			offset += PAGE_SIZE;
			selector = builder.increaseOffsetBy(PAGE_SIZE).build();
		} while (offset < page.getTotalNumEntries());

		return list;
	}

	/**
	 * 查询广告系列
	 */
	public Campaign getCampaign(Long id) throws ApiException, RemoteException {
		SelectorBuilder builder = new SelectorBuilder();
		Selector selector = builder.fields(CampaignField.values()).equalsId(id).limit(1).build();
		CampaignPage page = campaignService.get(selector);
		if (page.getEntries() != null) {
			return page.getEntries(0);
		} else {
			return null;
		}
	}

	/**
	 * 查询所有广告系列组
	 */
	public List<CampaignGroup> getAllCampaignGroup() throws ApiException, RemoteException {
		List<CampaignGroup> list = new ArrayList<CampaignGroup>();
		
		int offset = 0;
		SelectorBuilder builder = new SelectorBuilder();
		Selector selector = builder.fields(CampaignGroupField.values())
				.orderAscBy(CampaignGroupField.Name)
				.offset(offset).limit(PAGE_SIZE).build();

		CampaignGroupPage page;
		do {
			page = campaignGroupService.get(selector);
			if (page.getEntries() != null) {
				for (CampaignGroup campaign : page.getEntries()) {
					list.add(campaign);
				}
			}
			offset += PAGE_SIZE;
			selector = builder.increaseOffsetBy(PAGE_SIZE).build();
		} while (offset < page.getTotalNumEntries());

		return list;
	}

	/**
	 * 查询广告系列组
	 */
	public CampaignGroup getCampaignGroup(Long id) throws ApiException, RemoteException {
		SelectorBuilder builder = new SelectorBuilder();
		Selector selector = builder.fields(CampaignGroupField.values()).equalsId(id).limit(1).build();
		CampaignGroupPage page = campaignGroupService.get(selector);
		if (page.getEntries() != null) {
			return page.getEntries(0);
		} else {
			return null;
		}
	}

	/**
	 * 查询所有广告系列组效果目标
	 */
	public List<CampaignGroupPerformanceTarget> getAllCampaignGroupPerformanceTarget() throws ApiException, RemoteException {
		List<CampaignGroupPerformanceTarget> list = new ArrayList<CampaignGroupPerformanceTarget>();
		
		int offset = 0;
		SelectorBuilder builder = new SelectorBuilder();
		Selector selector = builder.fields(CampaignGroupPerformanceTargetField.values())
				.orderAscBy(CampaignGroupPerformanceTargetField.StartDate)
				.offset(offset).limit(PAGE_SIZE).build();

		CampaignGroupPerformanceTargetPage page;
		do {
			page = campaignGroupPerformanceTargetService.get(selector);
			if (page.getEntries() != null) {
				for (CampaignGroupPerformanceTarget campaign : page.getEntries()) {
					list.add(campaign);
				}
			}
			offset += PAGE_SIZE;
			selector = builder.increaseOffsetBy(PAGE_SIZE).build();
		} while (offset < page.getTotalNumEntries());

		return list;
	}

	/**
	 * 查询广告系列组效果目标
	 */
	public CampaignGroupPerformanceTarget getCampaignGroupPerformanceTarget(Long id) throws ApiException, RemoteException {
		SelectorBuilder builder = new SelectorBuilder();
		Selector selector = builder.fields(CampaignGroupPerformanceTargetField.values()).equalsId(id).limit(1).build();
		CampaignGroupPerformanceTargetPage page = campaignGroupPerformanceTargetService.get(selector);
		if (page.getEntries() != null) {
			return page.getEntries(0);
		} else {
			return null;
		}
	}

	/**
	 * 更新广告系列预算为非共享
	 * @param ids
	 */
	public void updateCampaignBudget(List<Long> ids) throws ApiException, RemoteException {
		for (Long id : ids) {
			Budget budget = new Budget();
		    budget.setName("test budget:" + System.currentTimeMillis());
		    Money budgetAmount = new Money();
		    budgetAmount.setMicroAmount(1_000_000L);
		    budget.setAmount(budgetAmount);
		    budget.setDeliveryMethod(BudgetBudgetDeliveryMethod.STANDARD);
		    budget.setIsExplicitlyShared(false); // 不与其他广告系列共享预算
		    BudgetOperation budgetOperation = new BudgetOperation();
		    budgetOperation.setOperand(budget);
		    budgetOperation.setOperator(Operator.ADD);
		    Long budgetId = budgetService.mutate(new BudgetOperation[] {budgetOperation}).getValue(0).getBudgetId();
		    budget.setBudgetId(budgetId);
		    
		    Campaign c = new Campaign();
		    c.setId(id);
		    c.setBudget(budget);
		    CampaignOperation operations = new CampaignOperation();
			operations.setOperator(Operator.SET);
			operations.setOperand(c);
			campaignService.mutate(new CampaignOperation[]{operations});
		};
	}

	public static void main(String[] args) throws ApiException, RemoteException, ValidationException {
		CampaignGroupService campaignGroupService = new CampaignGroupService("698-254-6209");

		org.joda.time.DateTime startDate = org.joda.time.DateTime.now();
		org.joda.time.DateTime endDate = org.joda.time.DateTime.now().plusDays(90);
		List<Long> campaignIds = new ArrayList<Long>();
		campaignIds.add(1654531137L); // compaign-for-performance-target-test0 PAUSED
		campaignIds.add(1654530936L); // compaign-for-performance-target-test1 PAUSED
		campaignIds.add(1654529949L); // compaign-for-performance-target-test2 PAUSED
		campaignIds.add(1654531089L); // compaign-for-performance-target-test3 PAUSED
		campaignIds.add(1654037906L); // compaign-for-performance-target-test4 PAUSED
		campaignIds.add(1654535919L); // compaign-for-performance-target-test5 ENABLED
		campaignIds.add(1654173511L); // compaign-for-performance-target-test6 ENABLED
		campaignIds.add(1654173304L); // compaign-for-performance-target-test7 ENABLED
		campaignIds.add(1654173520L); // compaign-for-performance-target-test8 ENABLED
		campaignIds.add(1654043462L); // compaign-for-performance-target-test9 ENABLED
		campaignIds.add(1654576005L); // compaign-for-performance-target-test10 PAUSED
		campaignIds.add(1654575774L); // compaign-for-performance-target-test11 PAUSED
		campaignIds.add(1654209796L); // compaign-for-performance-target-test12 PAUSED
		campaignIds.add(1654086473L); // compaign-for-performance-target-test13 PAUSED
		campaignIds.add(1654209775L); // compaign-for-performance-target-test14 PAUSED
		//campaignGroupService.updateCampaignBudget(campaignIds);
		campaignGroupService.createPerformanceTargetForCampaigns(campaignIds, 5000L, 2d, 100L, startDate.toString("yyyyMMdd"), endDate.toString("yyyyMMdd"));

		// 查询添加结果
		System.out.println("=====ALL Campaign=====");
		campaignGroupService.getAllCampaign().forEach(c -> {
			System.out.printf("%d-%s%n", c.getId(), c.getName());
		});
		System.out.println("=====ALL CampaignGroup=====");
		campaignGroupService.getAllCampaignGroup().forEach(cg -> {
			System.out.printf("%d-%s-%s%n", cg.getId(), cg.getName(), cg.getStatus());
		});
		System.out.println("=====ALL CampaignGroupPerformanceTarget=====");
		campaignGroupService.getAllCampaignGroupPerformanceTarget().forEach(cgpt -> {
			// 如果 forecastStatus 返回 ACTIVE_NEEDS_ATTENTION，则表示您的广告系列可能无法达到其目标，您应该进行检查以改善效果
			System.out.printf("%d-%s-%s%n", cgpt.getId(), cgpt.getCampaignGroupId(), cgpt.getPerformanceTarget().getForecastStatus().toString());
		});

	}

}

